package businessLogic;

import Definitions.Globals;
import Definitions.ManagerException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJBException;
import javax.ejb.SessionContext;
import javax.ejb.Stateful;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;
import model.Club;
import model.League;
import model.Manager;
import model.Offer;
import model.Player;
import model.Stadium;

/**
 * Stateful session beana aplikace Ice Hockey Manager. Stará se o aplikační logiku a operace nad databází
 * @author kidney
 */
@Stateful
@TransactionManagement(TransactionManagementType.BEAN)
public class ManagerBean implements ManagerBeanLocal {

    /**
     * Aktuální přihlášený manager - uživatel
     */
    private Manager manager = null;
    /**
     * Momentálně spravovaný(otevřený) klub
     */
    private Club currentClub = null;
    /**
     * Seznam klubů právě pihlášeného managera
     */
    private List<Club> clubs;
    @PersistenceContext
    private EntityManager em;
    @Resource
    private SessionContext ctx;
    @Resource
    UserTransaction utx;

    /**
     * Inicializace promennych beany a nacteni entity manageru
     * @param login
     * @throws ManagerException
     */
    @RolesAllowed({"user", "admin"})
    public void loginAndInit(String login) throws ManagerException {
        if (login == null) {
            throw new ManagerException("Unknown user!");
        }
        manager = em.find(Manager.class, login);
        clubs = new ArrayList<Club>();
        if (manager == null) {
            throw new ManagerException("No user found!");
        }
        if (manager.getClubCollection().isEmpty()) {
            currentClub = null;
        } else {
            clubs = new ArrayList(manager.getClubCollection());
            currentClub = clubs.iterator().next();
        }
    }

    /**
     * Vrátí kolekci klubů daného manažera. Vrací kolekci, ale používá se ArrayList
     * @return List<Club>
     */
    @RolesAllowed({"user", "admin"})
    public List<Club> getClubs() {
        return clubs;
    }

    /**
     * Vytvoří pro hráče nový klub se zadaným jménem, jménem stadionu a začlení ho do vybrané ligy.
     * Pokud selže(klub s daným názvem už existuje), vyhodí výjimku. Transakce
     * @param name String
     * @param stadiumName String
     * @param league League
     * @throws ManagerException
     * @return Club
     */
    @RolesAllowed({"user", "admin"})
    public Club createClub(String name, String stadiumName, League league) throws ManagerException {
        if (isClubNameAlreadyUsed(name)) {
            throw new ManagerException("Club with this name already exists.");
        }
        Club club = new Club();
        try {
            utx.begin();

            club.setName(name);
            club.setManager(manager);

            em.persist(club);

            Stadium stadium = new Stadium(club.getClubId());
            stadium.setName(stadiumName);
            stadium.setClub(club);
            club.setStadium(stadium);

            manager.getClubCollection().add(club);
            league.getClubCollection().add(club);
            club.getLeagueCollection().add(league);

            em.persist(stadium);
            generateSquad(club, league.getCountry());

            clubs.add(club);
            switchClub(club);

            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
        return club;
    }

    /**
     * Zvýší kapacitu stadionu o zadaný počet sedadel. Pokud operace selže(např. nejsou peníze), vyhodí výjimku
     * s popisem. Transakce
     * @param seatCount int
     * @throws ManagerException
     */
    @RolesAllowed({"user", "admin"})
    public void increaseStadiumCapacity(int seatCount) throws ManagerException {

        int cost = seatCount * Globals.COST_PER_SEAT;
        if (cost >= currentClub.getAccount().intValue()) {
            throw new ManagerException("Not enough money to buy " + seatCount + " seats.");
        }
        if ((currentClub.getStadium().getCapacity() + seatCount) >= Globals.MAX_STADIUM_SEATS) {
            throw new ManagerException("Maximum seat count exceeded. Maximum is " + Globals.MAX_STADIUM_SEATS + "seats.");
        }

        try {
            utx.begin();
            currentClub.getStadium().increaseCapacity(seatCount);
            currentClub.setAccount(new Integer(currentClub.getAccount() - cost));
            em.merge(currentClub.getStadium());
            em.merge(currentClub);
            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
    }

    /**
     * Zlepší o jednotku vybavení stadionu. Pokud operace selže(např. nejsou peníze, víc už vylepšovat nejde),
     * pak vyhodí výjimku s popisem. Transakce
     * @throws ManagerException
     */
    @RolesAllowed({"user", "admin"})
    public void improveStadiumEquipment() throws ManagerException {

        if (Globals.EQUIPMENT_LEVEL_COST >= currentClub.getAccount()) {
            throw new ManagerException("Not enough money to improve stadium equipment. " + "You need " + Globals.EQUIPMENT_LEVEL_COST + ".");
        }
        if (currentClub.getStadium().getEquipmentLevel() >= Globals.MAX_EQUIPMENT_LEVEL) {
            throw new ManagerException("Maximum equipment level reached.");
        }
        try {
            utx.begin();
            currentClub.getStadium().improveEquipment();
            currentClub.setAccount(currentClub.getAccount() - Globals.EQUIPMENT_LEVEL_COST);
            em.merge(currentClub.getStadium());
            em.merge(currentClub);
            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
    }

    /**
     * Přejmenuje momentálně spravovaný klub(ten co má uživatel teď otevřený). Pokud operace selže(klub s daným
     * názvem už existuje), vyhodí výjimku s popisem. Transakce
     * @param newName String
     * @throws ManagerException
     */
    @RolesAllowed({"user", "admin"})
    public void renameCurrentClub(String newName) throws ManagerException {
        if (currentClub.getName().equals(newName)) {
            return;
        }
        if (isClubNameAlreadyUsed(newName)) {
            throw new ManagerException("Club with this name already exists.");
        }
        try {
            utx.begin();
            currentClub.setName(newName);
            em.merge(currentClub);
            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
    }

    /**
     * Přejmenuje stadion momentálně spravovaného klubu.
     * @param newStadiumName String
     */
    @RolesAllowed({"user", "admin"})
    public void renameCurrentClubStadium(String newStadiumName) {
        if(currentClub.getStadium().getName().equals(newStadiumName)) {
            return;
        }
        try {
            utx.begin();
            currentClub.getStadium().rename(newStadiumName);
            em.merge(currentClub.getStadium());
            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
    }

    /**
     * Přepne správu klubu na jiný klub - klientovi se zobrazí údaje a editace klubu, který si vybral
     * @param clubName Club
     */
    @RolesAllowed({"user", "admin"})
    public void switchClub(Club club) {
        if (em.find(Club.class, club.getClubId()) == null) {
            return;
        }
        currentClub = club;
    }

    /**
     * Změní pozici hráče z týmu. Najde pozici, na kterou se má hráč přesunout a prohodí ho s hráčem, který
     * na ní do teď byl. Transakce
     * @param player Player
     * @param newPosition String
     */
    @RolesAllowed({"user", "admin"})
    public void changePlayerPosition(Player player, String newPosition) {
        if (em.find(Player.class, player.getPlayerId()) == null) //hrac nenalezen(pro jistotu:))
        {
            return;
        }
        try {
            utx.begin();

            String pos = player.getPositionInCurrentTeam();
            List<Player> pl = em.createQuery("SELECT p FROM Player p "
                    + "WHERE p.club=:klub AND p.positionInCurrentTeam = :pos").setParameter("klub", player.getClub()).setParameter("pos", newPosition).getResultList();

            if (pl.size() != 0) {
                Player p = pl.get(0);
                p.setPositionInCurrentTeam(pos);
                em.merge(p);
            }

            player.setPositionInCurrentTeam(newPosition);
            em.merge(player);

            em.flush();
            utx.commit();

        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
    }

    /**
     * Vytvoří nabídku na hráče cizího klubu. Vyhodí výjimku, pokud hráč nemá dost peněz
     * @param requestedPlayer Player
     * @param offeredPrice int
     * @throws ManagerException
     * @return Offer
     */
    @RolesAllowed({"user", "admin"})
    public Offer createOffer(Player requestedPlayer, int offeredPrice) throws ManagerException {
        if (offeredPrice >= currentClub.getAccount()) {
            throw new ManagerException("Not enough money to create this offer.");
        }
        if (em.find(Player.class, requestedPlayer.getPlayerId()) == null) {
            throw new ManagerException("This player does not exist.");
        }
        Offer offer;
        try {
            utx.begin();
            offer = new Offer();
            offer.setClub(currentClub);
            offer.setMoney(offeredPrice);
            offer.setPlayer(requestedPlayer);
            em.persist(offer);
            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
        return offer;
    }

    /**
     * Zruší podanou nabídku
     * @param offer Offer
     */
    @RolesAllowed({"user", "admin"})
    public void cancelOffer(Offer offer) {
        if (em.find(Offer.class, offer.getOfferId()) == null) {
            return;
        }
        try {
            utx.begin();
            em.remove(em.merge(offer));
            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
    }

    /**
     * Odmítne nabídku na hráče
     * Zatim vykona stejnou cinnost jako cancelOffer, treba doresit, jak to dat vedet zajemci
     * @param offer Offer
     */
    @RolesAllowed({"user", "admin"})
    public void denyOffer(Offer offer) {
        if (em.find(Offer.class, offer.getOfferId()) == null) {
            return;
        }
        try {
            utx.begin();
            em.remove(em.merge(offer));
            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
    }

    /**
     * Přijme nabídku na hráče. Vyhodí výjimku, pokud nabízená částka přesahuje majetek kupce. Transakce
     * @param offer Offer
     * @throws ManagerException
     * @return boolean
     */
    @RolesAllowed({"user", "admin"})
    public void acceptOffer(Offer offer) throws ManagerException {
        if (offer.getMoney() >= offer.getClub().getAccount()) {
            throw new ManagerException("The buyer has not enough money.");
        }

        Club seller = offer.getPlayer().getClub();
        if (offer.getPlayer().getPost().equals(Globals.GOALTENDER)) {
            if (getClubGoaltenders(seller).size() == Globals.MIN_GOALTENDERS_COUNT) {
                throw new ManagerException("You do not have enough goaltenders to sell one of them.");
            } else if (getClubFieldPlayers(seller).size() == Globals.MIN_FIELD_PLAYERS_COUNT) {
                throw new ManagerException("You do not have enough players to sell one of them.");
            }
        }

        try {
            utx.begin();
            offer.getClub().setAccount(offer.getClub().getAccount() - offer.getMoney());
            seller.setAccount(seller.getAccount() + offer.getMoney());
            seller.getPlayersCollection().remove(offer.getPlayer());
            offer.getClub().getPlayersCollection().add(offer.getPlayer());
            offer.getPlayer().setClub(offer.getClub());

            em.merge(seller);
            em.merge(offer.getClub());

            fillPosition(seller, offer.getPlayer().getPositionInCurrentTeam());

            if (offer.getPlayer().getPost().equals(Globals.GOALTENDER)) {
                offer.getPlayer().setPositionInCurrentTeam(Globals.RESERVE_GOALTENDER);
                em.merge(offer.getPlayer());
            } else {
                offer.getPlayer().setPositionInCurrentTeam(Globals.RESERVE_PLAYER);
                em.merge(offer.getPlayer());

                if (getClubFieldPlayers(seller).size() == 19) {
                    break4thLine(seller);
                }
                if (getClubFieldPlayers(offer.getClub()).size() == 20) {
                    make4thLine(offer.getClub());
                }
            }

            em.remove(em.merge(offer)); //obchod byl proveden, nabidka se muze zrusit
            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
    }

    /**
     * Zaplní danou pozici v sestavě náhradníkem (volá se po prodeji nějakého hráče)
     * @param c Club
     * @param position String
     */
    @RolesAllowed({"user", "admin"})
    private void fillPosition(Club c, String position) {
        if (position.equals(Globals.RESERVE_GOALTENDER) || position.equals(Globals.RESERVE_PLAYER)) {
            return;
        }
        if (position.equals(Globals.FIRST_GOALTENDER) || position.equals(Globals.SECOND_GOALTENDER)) {
            List<Player> reserveGoalies = em.createQuery("SELECT p FROM Player p "
                    + "WHERE p.club=:klub AND p.positionInCurrentTeam = :rGolman").setParameter("klub", c).setParameter("rGolman", Globals.RESERVE_GOALTENDER).getResultList();
            reserveGoalies.get(0).setPositionInCurrentTeam(position);
            return;
        }
        List<Player> substitutes = em.createQuery("SELECT p FROM Player p "
                + "WHERE p.club=:klub AND p.positionInCurrentTeam = :rPlayer").setParameter("klub", c).setParameter("rPlayer", Globals.RESERVE_PLAYER).getResultList();
        if (substitutes.size() == 0) {
            substitutes = em.createNativeQuery("SELECT * FROM Player p "
                    + "WHERE p.club_id = " + c.getClubId().toString()
                    + "AND p.position_in_current_team like '4__'", Player.class).getResultList();
        }
        substitutes.get(0).setPositionInCurrentTeam(position);
        em.merge(substitutes.get(0));
    }

    /**
     * Rozpustí čvrtou formaci (volá se, pokud již tým nemá dost hráčů na poskládání čtyř formací)
     * @param c Club
     */
    @RolesAllowed({"user", "admin"})
    private void break4thLine(Club c) {
        List<Player> restOf4thLine = em.createNativeQuery("SELECT * FROM Player p "
                + "WHERE p.club_id = " + c.getClubId().toString()
                + "AND p.position_in_current_team like '4__'", Player.class).getResultList();
        for (int i = 0; i < restOf4thLine.size(); i++) {
            restOf4thLine.get(i).setPositionInCurrentTeam(Globals.RESERVE_PLAYER);
            em.merge(restOf4thLine.get(i));
        }

    }

    /**
     * Sestaví čvrtou formaci (volá se, pokud už tým má dost hráčů na poskládání čtyř formací)
     * @param c Club
     */
    @RolesAllowed({"user", "admin"})
    private void make4thLine(Club c) {
        List<Player> substitutes = em.createQuery("SELECT p FROM Player p "
                + "WHERE p.club=:klub AND p.positionInCurrentTeam = :rPlayer").setParameter("klub", c).setParameter("rGolman", Globals.RESERVE_PLAYER).getResultList();
        substitutes.get(0).setPositionInCurrentTeam(Globals.LINE4_CENTRE);
        substitutes.get(1).setPositionInCurrentTeam(Globals.LINE4_LEFT_WING);
        substitutes.get(2).setPositionInCurrentTeam(Globals.LINE4_RIGHT_WING);
        substitutes.get(3).setPositionInCurrentTeam(Globals.LINE4_LEFT_DEFENCEMAN);
        substitutes.get(4).setPositionInCurrentTeam(Globals.LINE4_RIGHT_DEFENCEMAN);
        for (int i = 0; i < 5; i++) {
            em.merge(substitutes.get(i));
        }
    }

    /**
     * Najde a vrátí nabídky, které podal daný manažer
     * @return Collection<Offer>
     */
    @RolesAllowed({"user", "admin"})
    public List<Offer> lookupMyOffers() {
//        if (!em.contains(manager)) {
//            return null;
//        } else {
        List<Offer> offers = em.createQuery("SELECT o FROM Offer o "
                + "WHERE o.club=:klub AND :man_log=:manager").setParameter("klub", currentClub).setParameter("man_log", currentClub.getManager().getLogin()).setParameter("manager", manager.getLogin()).getResultList();
        return offers;
//        }
    }

    /**
     * Najde a vrátí nabídky na hráče, kteří hrají za kluby daného manažera
     * @return List<Offer>
     */
    @RolesAllowed({"user", "admin"})
    public List<Offer> lookupOffersForMyPlayers() {
//        if (!em.contains(manager)) {
//            return null;
//        } else {
        List<Offer> offers = em.createNativeQuery("SELECT * FROM Offer o WHERE o.player_id IN"
                + "(SELECT p.player_id FROM Player p WHERE p.club_id = "
                + currentClub.getClubId().toString() + ")",
                Offer.class).getResultList();
        return offers;
//        }
    }

    /**
     * Změna hesla manažera. Musí zadat původní a nové heslo. Vyhodí výjimku
     * s popisem, pokud např. zadal chybně původní heslo
     * @param oldPassword String
     * @param newPassword String
     * @throws ManagerException
     */
    @RolesAllowed({"user", "admin"})
    public void changePassword(String oldPassword, String newPassword) throws ManagerException {

        if (!oldPassword.equals(manager.getPassword())) {
            throw new ManagerException("Entered password does not correspond to the user's password.");
        }
        try {
            utx.begin();
            manager.setPassword(newPassword);
            em.merge(manager);
            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
    }

    /**
     * Vygeneruje hráče pro zadaný klub. Jejich jméno bude odpovídat zadané zemi.
     * @param club Club
     * @param country String
     * @throws ManagerException
     */
    @RolesAllowed({"user", "admin"})
    public void generateSquad(Club club, String country) throws ManagerException {
        List<String> names = em.createQuery("SELECT n.name FROM Name n WHERE n.origin=:requestedOrigin").setParameter("requestedOrigin", country).getResultList();
        List<String> surnames = em.createQuery("SELECT s.surname FROM Surname s WHERE s.origin=:requestedOrigin").setParameter("requestedOrigin", country).getResultList();
        if (names.isEmpty() || surnames.isEmpty()) {
            throw new ManagerException("There are no names for this country");
        }

        Random generator = new Random();
        Player[] squad = new Player[26];
        for (int i = 0; i < 26; i++) {
            squad[i] = new Player();
            squad[i].setClub(club);
            squad[i].setFirstName(names.get(generator.nextInt(names.size())));
            squad[i].setSurname(surnames.get(generator.nextInt(surnames.size())));
            squad[i].setNationality(country);
            squad[i].setAge(Globals.MIN_DEFAULT_AGE + generator.nextInt(Globals.MAX_DEFAULT_AGE - Globals.MIN_DEFAULT_AGE + 1));
        }

        squad[0].setPositionInCurrentTeam(Globals.FIRST_GOALTENDER);
        squad[1].setPositionInCurrentTeam(Globals.SECOND_GOALTENDER);
        squad[2].setPositionInCurrentTeam(Globals.RESERVE_GOALTENDER);
        squad[3].setPositionInCurrentTeam(Globals.LINE1_LEFT_DEFENCEMAN);
        squad[4].setPositionInCurrentTeam(Globals.LINE1_RIGHT_DEFENCEMAN);
        squad[5].setPositionInCurrentTeam(Globals.LINE1_CENTRE);
        squad[6].setPositionInCurrentTeam(Globals.LINE1_LEFT_WING);
        squad[7].setPositionInCurrentTeam(Globals.LINE1_RIGHT_WING);
        squad[8].setPositionInCurrentTeam(Globals.LINE2_LEFT_DEFENCEMAN);
        squad[9].setPositionInCurrentTeam(Globals.LINE2_RIGHT_DEFENCEMAN);
        squad[10].setPositionInCurrentTeam(Globals.LINE2_CENTRE);
        squad[11].setPositionInCurrentTeam(Globals.LINE2_LEFT_WING);
        squad[12].setPositionInCurrentTeam(Globals.LINE2_RIGHT_WING);
        squad[13].setPositionInCurrentTeam(Globals.LINE3_LEFT_DEFENCEMAN);
        squad[14].setPositionInCurrentTeam(Globals.LINE3_RIGHT_DEFENCEMAN);
        squad[15].setPositionInCurrentTeam(Globals.LINE3_CENTRE);
        squad[16].setPositionInCurrentTeam(Globals.LINE3_LEFT_WING);
        squad[17].setPositionInCurrentTeam(Globals.LINE3_RIGHT_WING);
        squad[18].setPositionInCurrentTeam(Globals.LINE4_LEFT_DEFENCEMAN);
        squad[19].setPositionInCurrentTeam(Globals.LINE4_RIGHT_DEFENCEMAN);
        squad[20].setPositionInCurrentTeam(Globals.LINE4_CENTRE);
        squad[21].setPositionInCurrentTeam(Globals.LINE4_LEFT_WING);
        squad[22].setPositionInCurrentTeam(Globals.LINE4_RIGHT_WING);
        squad[23].setPositionInCurrentTeam(Globals.RESERVE_PLAYER);
        squad[24].setPositionInCurrentTeam(Globals.RESERVE_PLAYER);
        squad[25].setPositionInCurrentTeam(Globals.RESERVE_PLAYER);

        for (int i = 0; i < 26; i++) {
            if (i < 3) {
                squad[i].setTechnique(0);
                squad[i].setStrength(0);
                squad[i].setSpeed(0);
                squad[i].setShot(0);
                squad[i].setGoalkeeping(generator.nextInt(Globals.MAX_DEAFULT_SKILL_VALUE + 1));
                squad[i].setPost(Globals.GOALTENDER);
            } else {
                squad[i].setTechnique(generator.nextInt(Globals.MAX_DEAFULT_SKILL_VALUE + 1));
                squad[i].setStrength(generator.nextInt(Globals.MAX_DEAFULT_SKILL_VALUE + 1));
                squad[i].setSpeed(generator.nextInt(Globals.MAX_DEAFULT_SKILL_VALUE + 1));
                squad[i].setShot(generator.nextInt(Globals.MAX_DEAFULT_SKILL_VALUE + 1));
                squad[i].setGoalkeeping(0);
                if (i == 3 || i == 8 || i == 13 || i == 18) {
                    squad[i].setPost(Globals.LEFT_DEFENCEMAN);
                } else if (i == 4 || i == 9 || i == 14 || i == 19) {
                    squad[i].setPost(Globals.RIGHT_DEFENCEMAN);
                } else if (i == 5 || i == 10 || i == 15 || i == 20) {
                    squad[i].setPost(Globals.CENTRE);
                } else if (i == 6 || i == 11 || i == 16 || i == 21) {
                    squad[i].setPost(Globals.LEFT_WING);
                } else if (i == 7 || i == 12 || i == 17 || i == 22) {
                    squad[i].setPost(Globals.RIGHT_WING);
                } else {
                    switch (generator.nextInt(5)) {
                        case 0:
                            squad[i].setPost(Globals.LEFT_DEFENCEMAN);
                            break;
                        case 1:
                            squad[i].setPost(Globals.RIGHT_DEFENCEMAN);
                            break;
                        case 2:
                            squad[i].setPost(Globals.CENTRE);
                            break;
                        case 3:
                            squad[i].setPost(Globals.LEFT_WING);
                            break;
                        default:
                            squad[i].setPost(Globals.RIGHT_WING);
                    }
                }
            }
            em.persist(squad[i]);
        }
    }

    /**
     * Inicializace pro nove vytvoreneno manazera - ulozeni jeho konta do databaze
     * @param manager
     */
    public void loginAndInitNewManager(Manager manager) {
        em.persist(manager);
        this.manager = manager;
        currentClub = null;
        clubs = new ArrayList<Club>();
    }

    /**
     * Vrátí právě aktivního manažera
     * @return Manager
     */
    @RolesAllowed({"user", "admin"})
    public Manager getManager() {
        return manager;
    }

    /**
     * Vrátí současně spravovaný klub
     * @return Club
     */
    @RolesAllowed({"user", "admin"})
    public Club getClub() {
        return currentClub;
    }

    /**
     * Vrátí všechny ligy z databáze
     * @return List<League>
     */
    @RolesAllowed({"user", "admin"})
    public List<League> getAllLeagues() {
        return (List<League>) em.createNamedQuery("League.findAll").getResultList();
    }

    /**
     * Vrátí ligy, kde je počet týmů nižší než povolená mez a kde nehraje žádný z týmu současného manažera
     * @return List<League>
     */
    @RolesAllowed({"user", "admin"})
    public List<League> getAvailableLeagues() {
        return (List<League>) em.createNativeQuery("SELECT * FROM League l WHERE ("
                + "SELECT COUNT(*) FROM Club c WHERE (SELECT COUNT(p) FROM Participation p WHERE p.club_id = c.club_id AND p.league_id = l.league_id) > 0) < " + Globals.MAX_CLUBS_IN_LEAGUE
                + " AND (SELECT COUNT(*) FROM Club k WHERE (SELECT COUNT(u) FROM Participation u WHERE u.club_id = k.club_id AND u.league_id = l.league_id) > 0 AND k.manager_login = '" + manager.getLogin() + "') = 0  ORDER BY l.name", League.class).getResultList();
    }

    /**
     * Vrátí všechny kluby ve vybrané lize
     * @param league
     * @return List<Club>
     */
    @RolesAllowed({"user", "admin"})
    public List<Club> getLeagueClubs(League league) {
        return (List<Club>) em.createNativeQuery("SELECT * FROM Club c WHERE (SELECT COUNT(p) FROM Participation p WHERE p.club_id = c.club_id AND p.league_id = " + league.getLeagueId().toString() + ") > 0", Club.class).getResultList();
    }

    /**
     * Vrátí všechny golmany chytající za daný klub
     * @param employer Club
     * @return List<Player>
     */
    @RolesAllowed({"user", "admin"})
    public List<Player> getClubGoaltenders(Club employer) {
        return (List<Player>) em.createNativeQuery("SELECT * FROM Player p WHERE p.club_id = "
                + employer.getClubId().toString() + " AND p.post = '" + Globals.GOALTENDER
                + "' ORDER BY p.position_in_current_team", Player.class).getResultList();
    }

    /**
     * Vrátí všechny hráče klubu kromě gólmanů
     * @param employer Club
     * @return List<Player>
     */
    @RolesAllowed({"user", "admin"})
    private List<Player> getClubFieldPlayers(Club employer) {
        return (List<Player>) em.createNativeQuery("SELECT * FROM Player p WHERE p.club_id = "
                + employer.getClubId().toString() + " AND p.post != '" + Globals.GOALTENDER
                + "' ORDER BY p.position_in_current_team", Player.class).getResultList();
    }

    /**
     * Vrátí všechny hráče klubu hrající v dané pětce
     * @param employer Club
     * @return List<Player>
     */
    @RolesAllowed({"user", "admin"})
    public List<Player> getClubLinePlayers(Club employer, Integer line) {
        List<Player> unsorted = (List<Player>) em.createNativeQuery("SELECT * FROM Player p WHERE p.club_id = "
                + employer.getClubId().toString() + " AND p.position_in_current_team like '"
                + line.toString() + "__' AND p.post != '" + Globals.GOALTENDER + "'", Player.class).getResultList();
        if (unsorted.size() != 5) {
            return new ArrayList<Player>();
        }
        List<Player> sorted = new ArrayList<Player>(unsorted);
        Player pl;
        String pos;
        for (int i = 0; i < 5; i++) {
            pl = unsorted.get(i);
            pos = pl.getPositionInCurrentTeam();
            if (pos.equals(line.toString() + Globals.CENTRE)) {
                sorted.set(0, pl);
            } else if (pos.equals(line.toString() + Globals.LEFT_WING)) {
                sorted.set(1, pl);
            } else if (pos.equals(line.toString() + Globals.RIGHT_WING)) {
                sorted.set(2, pl);
            } else if (pos.equals(line.toString() + Globals.LEFT_DEFENCEMAN)) {
                sorted.set(3, pl);
            } else if (pos.equals(line.toString() + Globals.RIGHT_DEFENCEMAN)) {
                sorted.set(4, pl);
            }
        }
        return sorted;
    }

    /**
     * Vrátí všechny hráče klubu, kteří jsou vedeni jako náhradníci
     * @param employer Club
     * @return List<Player>
     */
    @RolesAllowed({"user", "admin"})
    public List<Player> getClubSubstitutes(Club employer) {
        return (List<Player>) em.createNativeQuery("SELECT * FROM Player p WHERE p.club_id = "
                + employer.getClubId().toString() + " AND p.position_in_current_team = '"
                + Globals.RESERVE_PLAYER + "'", Player.class).getResultList();
    }

    /**
     * Vrátí ligu s daným id
     * @param id String
     * @return League
     */
    @RolesAllowed({"user", "admin"})
    public League getLeagueWithId(String id) {
        try {
            return em.find(League.class, Integer.parseInt(id));
        } catch (java.lang.NumberFormatException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Vrátí všechny pozice, na které v klubu může jít golman
     * @param c Club
     * @return List<String>
     */
    @RolesAllowed({"user", "admin"})
    public List<String> getPossibleGoaltenderPositionsInClub(Club c) {
        List<Player> goalmans = em.createNativeQuery("SELECT * FROM Player p WHERE p.club_id = " + c.getClubId().toString() + " AND p.post = '" + Globals.GOALTENDER + "' ORDER BY p.position_in_current_team", Player.class).getResultList();
        List<String> poses = new ArrayList<String>();
        for (Player p : goalmans) {
            if (!poses.contains(p.getPositionInCurrentTeam())) {
                poses.add(p.getPositionInCurrentTeam());
            }
        }
        return poses;
    }

    /**
     * Vrátí všechny možné pozice, na kterých může hrát hráč v klubu(kromě golmanů)
     * @param c Club
     * @return List<String>
     */
    @RolesAllowed({"user", "admin"})
    public List<String> getPossiblePositionsInClub(Club c) {
        List<Player> players = em.createNativeQuery("SELECT * FROM Player p WHERE p.club_id = " + c.getClubId().toString() + " AND p.post != '" + Globals.GOALTENDER + "' ORDER BY p.position_in_current_team", Player.class).getResultList();
        List<String> poses = new ArrayList<String>();
        for (Player p : players) {
            if (!poses.contains(p.getPositionInCurrentTeam())) {
                poses.add(p.getPositionInCurrentTeam());
            }
        }
        return poses;
    }

    /**
     * Vrátí true, pokud již existuje klub s daným jménem
     * @param clubName String
     * @return boolean
     */
    @RolesAllowed({"user", "admin"})
    private boolean isClubNameAlreadyUsed(String clubName) {
        List<Club> clb = em.createQuery("SELECT c FROM Club c WHERE c.name=:newName").setParameter("newName", clubName).getResultList();
        if (clb.isEmpty()) {
            return false;
        }
        // uživatel "přejmenovává" tým na jeho současný název, název se nemění, to je povoleno
        if (clb.get(0) == currentClub) {
            return false;
        }
        return true;
    }

    /**
     * Vytvoří v databázi novou ligu
     * @param leagueId int
     * @param leagueName String
     * @param leagueCountry String
     */
    @RolesAllowed({"admin"})
    public League createLeague(int leagueId, String leagueName, String leagueCountry) throws ManagerException {
        if (em.find(League.class, leagueId) != null) {
            throw new ManagerException("League with this ID already exists.");
        }
        League league = new League();
        try {
            utx.begin();
            league.setLeagueId(leagueId);
            league.setName(leagueName);
            league.setCountry(leagueCountry);
            em.persist(league);
            em.flush();
            utx.commit();
        } catch (Exception ex) {
            try {
                utx.rollback();
            } catch (SystemException syex) {
                throw new EJBException("Rollback failed: " + syex.getMessage());
            }
            throw new EJBException("Transaction failed: " + ex.getMessage());
        }
        return league;
    }

}
